/* * Copyright (C) 2003-2017 eXo Platform SAS. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.exoplatform.management.social.operations; import org.exoplatform.management.common.exportop.AbstractExportOperationHandler; import org.exoplatform.management.common.exportop.ActivitiesExportTask; import org.exoplatform.management.common.exportop.ExportTaskWrapper; import org.exoplatform.management.common.exportop.SpaceMetadataExportTask; import org.exoplatform.management.common.importop.AbstractJCRImportOperationHandler; import org.exoplatform.management.social.SocialExtension; import org.exoplatform.services.jcr.RepositoryService; import org.exoplatform.services.jcr.ext.distribution.DataDistributionManager; import org.exoplatform.services.jcr.ext.distribution.DataDistributionMode; import org.exoplatform.services.jcr.ext.distribution.DataDistributionType; import org.exoplatform.services.jcr.ext.hierarchy.NodeHierarchyCreator; import org.exoplatform.social.common.RealtimeListAccess; import org.exoplatform.social.core.activity.model.ExoSocialActivity; import org.exoplatform.social.core.identity.model.Identity; import org.exoplatform.social.core.identity.provider.SpaceIdentityProvider; import org.exoplatform.social.core.manager.ActivityManager; import org.exoplatform.social.core.manager.IdentityManager; import org.exoplatform.social.core.model.AvatarAttachment; import org.exoplatform.social.core.space.model.Space; import org.exoplatform.social.core.space.spi.SpaceService; import org.gatein.common.logging.Logger; import org.gatein.common.logging.LoggerFactory; import org.gatein.management.api.ContentType; import org.gatein.management.api.PathAddress; import org.gatein.management.api.controller.ManagedRequest; import org.gatein.management.api.controller.ManagedResponse; import org.gatein.management.api.controller.ManagementController; import org.gatein.management.api.exceptions.OperationException; import org.gatein.management.api.exceptions.ResourceNotFoundException; import org.gatein.management.api.operation.OperationAttributes; import org.gatein.management.api.operation.OperationContext; import org.gatein.management.api.operation.OperationNames; import org.gatein.management.api.operation.ResultHandler; import org.gatein.management.api.operation.model.ExportResourceModel; import org.gatein.management.api.operation.model.ExportTask; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.jcr.LoginException; import javax.jcr.NoSuchWorkspaceException; import javax.jcr.Node; import javax.jcr.PathNotFoundException; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.ValueFormatException; /** * The Class SocialDataExportResource. * * @author <a href="mailto:bkhanfir@exoplatform.com">Boubaker Khanfir</a> * @version $Revision$ */ public class SocialDataExportResource extends AbstractExportOperationHandler { /** The Constant log. */ final private static Logger log = LoggerFactory.getLogger(SocialDataExportResource.class); /** The Constant GROUPS_PATH. */ private static final String GROUPS_PATH = "groupsPath"; /** The management controller. */ private ManagementController managementController; /** The node hierarchy creator. */ private NodeHierarchyCreator nodeHierarchyCreator; /** The data distribution manager. */ private DataDistributionManager dataDistributionManager; // TODO For Space Dashboard export/import // private DataStorage dataStorage; /** * {@inheritDoc} */ @Override public void execute(OperationContext operationContext, ResultHandler resultHandler) throws ResourceNotFoundException, OperationException { spaceService = operationContext.getRuntimeContext().getRuntimeComponent(SpaceService.class); managementController = operationContext.getRuntimeContext().getRuntimeComponent(ManagementController.class); nodeHierarchyCreator = operationContext.getRuntimeContext().getRuntimeComponent(NodeHierarchyCreator.class); dataDistributionManager = operationContext.getRuntimeContext().getRuntimeComponent(DataDistributionManager.class); repositoryService = operationContext.getRuntimeContext().getRuntimeComponent(RepositoryService.class); activityManager = operationContext.getRuntimeContext().getRuntimeComponent(ActivityManager.class); identityManager = operationContext.getRuntimeContext().getRuntimeComponent(IdentityManager.class); // TODO For Space Dashboard export/import // dataStorage = // operationContext.getRuntimeContext().getRuntimeComponent(DataStorage.class); OperationAttributes attributes = operationContext.getAttributes(); List<String> operationFilters = attributes.getValues("filter"); // "replace-existing" attribute. Defaults to false. boolean exportCalendar = false, exportForum = false; if (operationFilters != null) { exportForum = operationFilters.contains("export-forum:true"); exportCalendar = operationFilters.contains("export-calendar:true"); } String spaceDisplayName = operationContext.getAddress().resolvePathTemplate("space-name"); List<ExportTask> exportTasks = new ArrayList<ExportTask>(); // Increase current transaction timeout increaseCurrentTransactionTimeOut(operationContext); try { Space space = spaceService.getSpaceByDisplayName(spaceDisplayName); String[] apps = space.getApp().split(","); Set<String> appsSet = new HashSet<String>(Arrays.asList(apps)); Map<String, List<String>> attributesMap = new HashMap<String, List<String>>(); attributesMap.put("exclude-space-metadata", Collections.singletonList("true")); List<String> filters = new ArrayList<String>(); filters.add(spaceDisplayName); attributesMap.put("filter", filters); Set<String> alreadyExportedPaths = new HashSet<String>(); for (String application : appsSet) { String path = getEntryResourcePath(application); if (path == null || (path.equals(SocialExtension.FORUM_RESOURCE_PATH) && !exportForum) || (path.equals(SocialExtension.CALENDAR_RESOURCE_PATH) && !exportCalendar)) { continue; } if (!alreadyExportedPaths.contains(path)) { addResourceExportTasks(exportTasks, attributesMap, path, space.getPrettyName()); alreadyExportedPaths.add(path); } // TODO Export/import Dashboard gadgets // else if (application.contains(SocialExtension.DASHBOARD_PORTLET)) { // Dashboard dashboard = // SocialDashboardExportTask.getDashboard(dataStorage, // space.getGroupId()); // if (dashboard == null) { // continue; // } // UIContainer uiContainer = new UIContainer(); // PortalDataMapper.toUIContainer(uiContainer, dashboard); // cleanupUIComponentFields(uiContainer); // exportTasks.add(new SocialDashboardExportTask(uiContainer, // space.getPrettyName())); // } } log.info("export space JCR private data"); computeContentFilters(space, attributesMap); addResourceExportTasks(exportTasks, attributesMap, SocialExtension.CONTENT_RESOURCE_PATH, space.getPrettyName()); log.info("export space MOP data (layout & Pages & navigation)"); attributesMap.clear(); addResourceExportTasks(exportTasks, attributesMap, SocialExtension.SITES_RESOURCE_PATH + space.getGroupId(), space.getPrettyName()); log.info("export space metadata"); String prefix = "social/space/" + space.getPrettyName() + "/"; exportTasks.add(new SpaceMetadataExportTask(space, prefix)); Identity spaceIdentity = identityManager.getOrCreateIdentity(SpaceIdentityProvider.NAME, space.getPrettyName(), false); log.info("export space avatar"); exportSpaceAvatar(exportTasks, space, spaceIdentity); log.info("export space activities"); exportSpaceActivities(exportTasks, space, spaceIdentity); } catch (Exception e) { throw new OperationException(OperationNames.EXPORT_RESOURCE, "Can't export Space", e); } finally { restoreDefaultTransactionTimeOut(operationContext); } resultHandler.completed(new ExportResourceModel(exportTasks)); } /** * Export space activities. * * @param exportTasks the export tasks * @param space the space * @param spaceIdentity the space identity */ private void exportSpaceActivities(List<ExportTask> exportTasks, Space space, Identity spaceIdentity) { RealtimeListAccess<ExoSocialActivity> spaceActivitiesList = activityManager.getActivitiesOfSpaceWithListAccess(spaceIdentity); ExoSocialActivity[] activities = null; int size = spaceActivitiesList.getSize(), i = 0; List<ExoSocialActivity> activitiesList = new ArrayList<ExoSocialActivity>(); while (i < size) { int length = i + 10 < size ? 10 : size - i; activities = spaceActivitiesList.load(i, length); for (ExoSocialActivity activity : activities) { // Don't export application activities if (activity.getType().equals(CONTENT_ACTIVITY_TYPE) || activity.getType().equals(FILE_ACTIVITY_TYPE) || activity.getType().equals(FORUM_ACTIVITY_TYPE) || activity.getType().equals(POLL_ACTIVITY_TYPE) || activity.getType().equals(WIKI_ACTIVITY_TYPE) || activity.getType().equals(CALENDAR_ACTIVITY_TYPE)) { continue; } addActivityWithComments(activitiesList, activity); } i += length; } String prefix = "social/space/" + space.getPrettyName() + "/"; exportTasks.add(new ActivitiesExportTask(identityManager, activitiesList, prefix)); } /** * Export space avatar. * * @param exportTasks the export tasks * @param space the space * @param spaceIdentity the space identity * @throws UnsupportedEncodingException the unsupported encoding exception * @throws Exception the exception * @throws PathNotFoundException the path not found exception * @throws RepositoryException the repository exception * @throws ValueFormatException the value format exception */ private void exportSpaceAvatar(List<ExportTask> exportTasks, Space space, Identity spaceIdentity) throws UnsupportedEncodingException, Exception, PathNotFoundException, RepositoryException, ValueFormatException { // No method to get avatar using Social API, so we have to use JCR String avatarURL = spaceIdentity.getProfile().getAvatarUrl(); avatarURL = avatarURL == null ? null : URLDecoder.decode(avatarURL, "UTF-8"); if (avatarURL != null && avatarURL.contains(space.getPrettyName())) { int beginIndexAvatarPath = avatarURL.indexOf("repository/social") + ("repository/social").length(); int endIndexAvatarPath = avatarURL.indexOf("?"); String avatarNodePath = endIndexAvatarPath >= 0 ? avatarURL.substring(beginIndexAvatarPath, endIndexAvatarPath) : avatarURL.substring(beginIndexAvatarPath); Session session = AbstractJCRImportOperationHandler.getSession(repositoryService, "social"); Node avatarNode = (Node) session.getItem(avatarNodePath); Node avatarJCRContentNode = avatarNode.getNode("jcr:content"); String fileName = "avatar"; String mimeType = avatarJCRContentNode.hasProperty("jcr:data") ? avatarJCRContentNode.getProperty("jcr:mimeType").getString() : null; InputStream inputStream = avatarJCRContentNode.hasProperty("jcr:data") ? avatarJCRContentNode.getProperty("jcr:data").getStream() : null; Calendar lastModified = avatarJCRContentNode.hasProperty("jcr:data") ? avatarJCRContentNode.getProperty("jcr:lastModified").getDate() : null; AvatarAttachment avatar = new AvatarAttachment(null, fileName, mimeType, inputStream, null, lastModified.getTimeInMillis()); exportTasks.add(new SpaceAvatarExportTask(space.getPrettyName(), avatar)); } } /** * Gets the entry resource path. * * @param application the application * @return the entry resource path */ private String getEntryResourcePath(String application) { String path = null; if (application.contains(SocialExtension.FORUM_PORTLET)) { path = SocialExtension.FORUM_RESOURCE_PATH; log.info("export space forum data"); } else if (application.contains(SocialExtension.CALENDAR_PORTLET)) { path = SocialExtension.CALENDAR_RESOURCE_PATH; log.info("export space calendar data"); } else if (application.contains(SocialExtension.WIKI_PORTLET)) { path = SocialExtension.WIKI_RESOURCE_PATH; log.info("export space wiki data"); } return path; } /** * Compute content filters. * * @param space the space * @param attributesMap the attributes map * @throws RepositoryException the repository exception * @throws LoginException the login exception * @throws NoSuchWorkspaceException the no such workspace exception * @throws PathNotFoundException the path not found exception */ private void computeContentFilters(Space space, Map<String, List<String>> attributesMap) throws RepositoryException, LoginException, NoSuchWorkspaceException, PathNotFoundException { List<String> filters = attributesMap.get("filter"); DataDistributionType dataDistributionType = dataDistributionManager.getDataDistributionType(DataDistributionMode.NONE); String contentWorkspace = repositoryService.getCurrentRepository().getConfiguration().getDefaultWorkspaceName(); String groupsPath = nodeHierarchyCreator.getJcrPath(GROUPS_PATH); Session session = AbstractJCRImportOperationHandler.getSession(repositoryService, contentWorkspace); Node groupRootNode = (Node) session.getItem(groupsPath); Node spaceNode = dataDistributionType.getDataNode(groupRootNode, space.getGroupId()); filters.clear(); filters.add("query:select * from nt:base where jcr:path = '" + spaceNode.getPath() + "'"); filters.add("workspace:" + contentWorkspace); filters.add("taxonomy:false"); filters.add("validate-structure:false"); attributesMap.put("excludePaths", Collections.singletonList(spaceNode.getPath() + "/ApplicationData/eXoWiki")); } /** * Adds the resource export tasks. * * @param exportTasks the export tasks * @param attributesMap the attributes map * @param path the path * @param spaceId the space id */ private void addResourceExportTasks(List<ExportTask> exportTasks, Map<String, List<String>> attributesMap, String path, String spaceId) { ManagedRequest request = ManagedRequest.Factory.create(OperationNames.EXPORT_RESOURCE, PathAddress.pathAddress(path), attributesMap, ContentType.ZIP); ManagedResponse response = managementController.execute(request); ExportResourceModel model = (ExportResourceModel) response.getResult(); List<ExportTask> entryExportTasks = model.getTasks(); String basePath = SocialExtension.SPACE_RESOURCE_PARENT_PATH + "/" + spaceId + "/"; for (ExportTask exportTask : entryExportTasks) { exportTasks.add(new ExportTaskWrapper(exportTask, basePath)); } } }